package com.one.blocks.rbac.datascope;

import com.one.blocks.rbac.manager.SysRoleManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * <a href="https://mybatis.org/mybatis-3/configuration.html#plugins">mybatis plugin官网介绍</a>
 *
 * @author <a href="mailto:idler41@163.con">linfuxin</a> created on 2023-12-29 09:40:27
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
@Slf4j
public class DataScopeInterceptor implements Interceptor, ApplicationContextAware {

    /**
     * 这是指定拦截位置的注解@Signature中的args的序号
     */
    public static final int PARAMETER_INDEX_MAPPED_STATEMENT = 0;
    public static final int PARAMETER_INDEX_PARAMETER = 1;
    public static final int PARAMETER_INDEX_ROW_BOUNDS = 2;
    public static final int PARAMETER_INDEX_RESULT_HANDLER = 3;
    public static final int PARAMETER_INDEX_CACHE_KEY = 4;
    public static final int PARAMETER_INDEX_BOUND_SQL = 5;

    private SysRoleManager sysRoleManager;
    private Map<String, DataScope> dataScopeCache;
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (dataScopeCache == null) {
//            initIfNecessary();
        }
        // 获取到拦截方法的参数
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[PARAMETER_INDEX_MAPPED_STATEMENT];
        // 只拦截select语句
        if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType()) {
            return invocation.proceed();
        }
        DataScope dataScope = dataScopeCache.get(mappedStatement.getId());
        // 只处理注解修饰语句
        if (dataScope == null) {
            return invocation.proceed();
        }
        if (log.isDebugEnabled()) {
            log.debug("dataScope plugin enable");
        }
        return invocation.proceed();
        // 创建sql参数的map
//        args[PARAMETER_INDEX_PARAMETER] = createSqlParameterMap(args, dataScope);
//        // 运行新的调用对象（参数被覆盖）
//        return new Invocation(invocation.getTarget(), invocation.getMethod(), args).proceed();
    }

    private synchronized void initIfNecessary() {
        if (dataScopeCache == null) {
            dataScopeCache = new HashMap<>(256);
            this.sysRoleManager = applicationContext.getBean(SysRoleManager.class);
            SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
            Configuration configuration = sqlSessionFactory.getConfiguration();
            for (MappedStatement mappedStatement : configuration.getMappedStatements()) {
                DataScope dataScope = resolveDataScopeIfPossible(mappedStatement);
                if (dataScope != null) {
                    dataScopeCache.put(mappedStatement.getId(), dataScope);
                }
            }
        }
    }

//    private Map<String, Object> createSqlParameterMap(Object[] args, DataScope dataScope) {
//
//        Map<String, Object> parameterMap = new HashMap<>();
//        // 当执行的mapper接口方法有参数时
//        if (args[PARAMETER_INDEX_PARAMETER] != null) {
//            // 如果是多个值会存储在map中
//            if (args[PARAMETER_INDEX_PARAMETER] instanceof Map) {
//                //noinspection unchecked,rawtypes
//                parameterMap = (Map) args[PARAMETER_INDEX_PARAMETER];
//                if (!dataScope.requestParam().equals("-undefined-")) {
//                    // 单个数组集合作为参数，则需要添加命名参数
//                    parameterMap.put(dataScope.requestParam(), parameterMap.get(dataScope.argIndex()));
//                }
//            } else if (BeanUtils.isSimpleValueType(args[PARAMETER_INDEX_PARAMETER].getClass())) {
//                parameterMap.putIfAbsent(dataScope.requestParam(), args[PARAMETER_INDEX_PARAMETER]);
//            } else {
//                // 如果是DTO类型，反射获取字段和值的map
//                Object dto = args[PARAMETER_INDEX_PARAMETER];
//                // 获取属性
//                PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(dto.getClass());
//                for (PropertyDescriptor p : propertyDescriptors) {
//                    // spring的BeanUtils.getPropertyDescriptors会把class也获取到
//                    if (p.getPropertyType().equals(Class.class)) {
//                        continue;
//                    }
//
//                    Object value = null;
//                    try {
//                        // 读取实际值
//                        value = p.getReadMethod().invoke(dto);
//                    } catch (IllegalAccessException | InvocationTargetException e) {
//                        e.printStackTrace();
//                    }
//                    parameterMap.putIfAbsent(p.getName(), value);
//                }
//            }
//        }
//
//        additionalParam(parameterMap, dataScope);
//        return parameterMap;
//    }
//
//    protected void additionalParam(Map<String, Object> parameterMap, DataScope dataScope) {
//        // todo 权限类型 => 0-本人数据 1-本组织数据 2-本组织及以下数据 3-自定义数据 4-全部数据
//        parameterMap.put(dataScope.scopeParam(), Arrays.asList("org1", "org2"));
//    }

    private DataScope resolveDataScopeIfPossible(MappedStatement mappedStatement) {
        int methodStartIndex = mappedStatement.getId().lastIndexOf(".");
        Class<?> mapper = null;
        try {
            mapper = Class.forName(mappedStatement.getId().substring(0, methodStartIndex));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        String methodName = mappedStatement.getId().substring(methodStartIndex + 1);
        for (Method method : mapper.getDeclaredMethods()) {
            if (method.isAnnotationPresent(DataScope.class) && (methodName.equals(method.getName()))) {
                return method.getAnnotation(DataScope.class);
            }
        }
        return null;
    }
}
